///-------------------------------------------------------------------------------------------------
// file:	Peer\Peer.h
//
// summary:	Declares the peer class
///-------------------------------------------------------------------------------------------------

#pragma once 

#include <string>
#include <hash_map>
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <tuple>

///For ThreadPool
//#include <boost/asio/io_service.hpp>
//#include <boost/bind.hpp>
//#include <boost/thread/thread.hpp>


#include <boost\tokenizer.hpp>
#include <boost\lexical_cast\lexical_cast_old.hpp>


#include "..\Raknet\PacketPriority.h"
#include "..\Raknet\RakPeerInterface.h"
#include "..\Raknet\MessageIdentifiers.h"
#include "..\Raknet\BitStream.h"
#include "..\UtilityClasses\Common.h"
#include "..\Peer\HelperClass.h"
#include "../Peer/Neighbors/Neighbour.h"

///-------------------------------------------------------------------------------------------------
/// <summary>	A macro that defines thread pool amount threads. </summary>
///
/// <remarks>	Adrian, 31/08/2015. </remarks>
///-------------------------------------------------------------------------------------------------

#define THREAD_POOL_AMOUNT_THREADS 1

using namespace RakNet;
using std::hash_map;
using std::cout;
using std::endl;
using std::vector;

/// <summary>	Identifier for the boot strap peer. </summary>
const static std::string BOOT_STRAP_PEER_ID = "bootStrap";

///-------------------------------------------------------------------------------------------------
/// <summary>	Defines an alias representing the tokenizer. </summary>
///
/// <remarks>	Adrian, 31/08/2015. </remarks>
///-------------------------------------------------------------------------------------------------

typedef boost::tokenizer<boost::char_separator<char>> tokenizer;

namespace Network
{
	namespace Peer
	{
		///-------------------------------------------------------------------------------------------------
		/// <summary>	Values that represent peer messages. </summary>
		///
		/// <remarks>	Adrian, 31/08/2015. </remarks>
		///-------------------------------------------------------------------------------------------------

		enum PeerMessages
		{
			/// <summary>	An enum constant representing the boot hand shake request option. </summary>
			BOOT_HAND_SHAKE_REQUEST = ID_USER_PACKET_ENUM + 1,
			/// <summary>	An enum constant representing the recieve boot strap reply option. </summary>
			RECIEVE_BOOT_STRAP_REPLY,
			/// <summary>	An enum constant representing the send update to peer option. </summary>
			SEND_UPDATE_TO_PEER,
			//SEND_PING_PEER,
			/// <summary>	An enum constant representing the request neighbors option. </summary>
			REQUEST_NEIGHBORS,
			/// <summary>	An enum constant representing the receive peers option. </summary>
			RECEIVE_PEERS,
			/// <summary>	An enum constant representing the reply to peers option. </summary>
			REPLY_TO_PEERS,
			/// <summary>	An enum constant representing the hand shake initialise update option. </summary>
			HAND_SHAKE_INIT_UPDATE,
			/// <summary>	An enum constant representing the hand shake reply peer option. </summary>
			HAND_SHAKE_REPLY_PEER,
			/// <summary>	An enum constant representing the share local updates option. </summary>
			SHARE_LOCAL_UPDATES,
			/// <summary>	An enum constant representing the share local updates reply option. </summary>
			SHARE_LOCAL_UPDATES_REPLY,
			//RECIEVE_PEERS,
			/// <summary>	An enum constant representing the test message option. </summary>
			TEST_MESSAGE
		};

		///-------------------------------------------------------------------------------------------------
		/// <summary>	A peer clas sfor a distributed system. </summary>
		///
		/// <remarks>	Adrian, 31/08/2015. </remarks>
		///-------------------------------------------------------------------------------------------------
		class Peer
		{
		public:

			Peer()
			{
				peerData = RakPeerInterface::GetInstance();
				portNumber = LISTENING_PORT;
				newToNetwork = false;
				SocketDescriptor peerSocket;
				peerSocket = SocketDescriptor(LISTENING_PORT, 0);
				peerData->Startup(MAXIMUM_AMOUNT_NEIGHBORS, &peerSocket, 1);

				//set maximum number of connections
				peerData->SetMaximumIncomingConnections(MAXIMUM_AMOUNT_NEIGHBORS);

				cout << "Peer established" << endl;
				running = true;
				//spawn update thread
				ListenOnNetwork();
			}

			//new peer will connect to gate keeper first

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Constructor. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="ipAddress">   	The IP address. </param>
			/// <param name="socketNumber">	The socket number. </param>
			///-------------------------------------------------------------------------------------------------

			Peer(std::string ipAddress, int socketNumber)
			{
				peerData = RakPeerInterface::GetInstance();
				portNumber = LISTENING_PORT + socketNumber;
				bootStrapAddress = ipAddress;
				newToNetwork = true;
				SocketDescriptor peerSocket;
				peerSocket = SocketDescriptor(LISTENING_PORT + socketNumber, 0);
				peerData->Startup(MAXIMUM_AMOUNT_NEIGHBORS, &peerSocket, 1);

				//set maximum number of connections
				peerData->SetMaximumIncomingConnections(MAXIMUM_AMOUNT_NEIGHBORS);
				running = true;
				cout << "Peer established" << endl;
				//spawn update thread
				ListenOnNetwork();
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Constructor. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="ipAddress">   	The IP address. </param>
			/// <param name="socketNumber">	The socket number. </param>
			/// <param name="nameAssigned">	The name assigned. </param>
			///-------------------------------------------------------------------------------------------------

			Peer(std::string ipAddress, int socketNumber, std::string nameAssigned)
			{
				peerData = RakPeerInterface::GetInstance();
				portNumber = LISTENING_PORT + socketNumber;
				bootStrapAddress = ipAddress;
				newToNetwork = true;
				SocketDescriptor peerSocket;
				peerSocket = SocketDescriptor(LISTENING_PORT + socketNumber, 0);
				peerData->Startup(MAXIMUM_AMOUNT_NEIGHBORS, &peerSocket, 1);

				//set maximum number of connections
				peerData->SetMaximumIncomingConnections(MAXIMUM_AMOUNT_NEIGHBORS);
				running = true;
				cout << "Peer " << nameAssigned << " established" << endl;
				//spawn update thread
				ListenOnNetwork();
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Constructor. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="socketNumber">	The socket number. </param>
			///-------------------------------------------------------------------------------------------------

			Peer(int socketNumber)
			{
				peerData = RakPeerInterface::GetInstance();
				portNumber = socketNumber;
				newToNetwork = true;
				SocketDescriptor peerSocket;
				peerSocket = SocketDescriptor(LISTENING_PORT + socketNumber, 0);
				peerData->Startup(MAXIMUM_AMOUNT_NEIGHBORS, &peerSocket, 1);

				//set maximum number of connections
				peerData->SetMaximumIncomingConnections(MAXIMUM_AMOUNT_NEIGHBORS);

				cout << "Peer established" << endl;
				running = true;
				//spawn update thread
				ListenOnNetwork();
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Constructor. (test constructor) </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="bootStrapIdAddress">	The boot strap identifier address. </param>
			///-------------------------------------------------------------------------------------------------
			Peer(std::string bootStrapIdAddress)
			{
				peerData = RakPeerInterface::GetInstance();
				//define the maximhum almount of connections that  apeer can have 
				newToNetwork = true;
				//AddNeighbor(BOOT_STRAP_PEER_ID, bootStrapIdAddress);
				//ListenOnNetwork();
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Destructor. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///-------------------------------------------------------------------------------------------------
			~Peer()
			{
				RakPeerInterface::DestroyInstance(peerData);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Hand shake request. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="address">	[in,out] The address. </param>
			///-------------------------------------------------------------------------------------------------

			void HandShakeRequest(SystemAddress& address)
			{
				BitStream bS;
				bS.Write((RakNet::MessageID)BOOT_HAND_SHAKE_REQUEST);
				SendData(address, bS);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Deal hand shake. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="address">	[in,out] The address. </param>
			/// <param name="ddress"> 	[in,out] The ddress. </param>
			///-------------------------------------------------------------------------------------------------

			void DealHandShake(SystemAddress address, RakNetGUID ddress)
			{
				//add bootstrap as a neighbour
				AddNeighbor(ddress, address);

				BitStream bS;
				bS.Write((RakNet::MessageID)RECIEVE_BOOT_STRAP_REPLY);
				std::string output = GetRandomNeighbors();
				bS.Write(output.c_str());
				peerData->Send(&bS, HIGH_PRIORITY, RELIABLE_ORDERED, 0, address, false);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Adds a neighbor to 'address'. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="peerName">	[in,out] Name of the peer. </param>
			/// <param name="address"> 	[in,out] The address. </param>
			///-------------------------------------------------------------------------------------------------

			void AddNeighbor(RakNetGUID& peerName, SystemAddress& address)
			{
				int testInt = 0;
				if (!FindNeighbour(address, testInt) && peerName != peerData->GetMyGUID())
				{
					if (neighbors.size() >= MAXIMUM_AMOUNT_NEIGHBORS)
					{
						SortCache();
						RemoveNeighbour(neighbors.size() - 1);
					}

					neighbors.push_back(Neighbour(address, peerName));
				}
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Removes the neighbour described by index. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="index">	Zero-based index of the. </param>
			///-------------------------------------------------------------------------------------------------

			void RemoveNeighbour(int index)
			{
				neighbors.erase(neighbors.begin() + index);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Searches for the first neighbour. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="address">	[in,out] The address. </param>
			/// <param name="index">  	[in,out] Zero-based index of the. </param>
			///
			/// <returns>	true if it succeeds, false if it fails. </returns>
			///-------------------------------------------------------------------------------------------------

			bool FindNeighbour(SystemAddress& address, int& index)
			{
				vector<Neighbour>::iterator iter = neighbors.begin();
				index = 0;
				for (iter; iter != neighbors.end(); iter++)
				{
					if ((*iter).address == address)
					{
						return true;
					}
					++index;
				}

				if (iter != neighbors.end())
					return true;

				return false;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Listen on network. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///-------------------------------------------------------------------------------------------------

			void ListenOnNetwork()
			{
				Packet *packet;
				//update thread
				//std::thread updateThread(&Peer::SendUpdate, this);
				//InitPool();
				HelperClass helperClass;

				if (newToNetwork)
				{
					peerData->Connect(bootStrapAddress.c_str(), LISTENING_PORT, 0, 0);
				}

				while (1)
				{
					for (packet = peerData->Receive(); packet; peerData->DeallocatePacket(packet), packet = peerData->Receive())
					{
						switch (packet->data[0])
						{
						case ID_REMOTE_DISCONNECTION_NOTIFICATION:
							printf("Another client has disconnected.\n");
							break;

						case ID_REMOTE_CONNECTION_LOST:
							printf("Another client has lost the connection.\n");
							break;

						case ID_REMOTE_NEW_INCOMING_CONNECTION:
							printf("Another client has connected.\n");
							break;

							//connection accepted by a peer
						case ID_CONNECTION_REQUEST_ACCEPTED:
						{
															   printf("Our connection request has been accepted");

															   //check if we need to exchnage neighbors
															   if (newToNetwork)
															   {
																   AddNeighbor(packet->guid, packet->systemAddress);
																   HandShakeRequest(packet->systemAddress);
																   newToNetwork = !newToNetwork;
															   }
						}

							printf("Our connection request has been accepted.\n");
							break;

						case ID_NEW_INCOMING_CONNECTION:
							printf("A connection is incoming %s .\n", packet->systemAddress.ToString());
							break;

						case ID_NO_FREE_INCOMING_CONNECTIONS:
							printf("The server is full .\n");
							break;
						case ID_DISCONNECTION_NOTIFICATION:
							printf("We have been disconnected.\n");
							break;
						case ID_CONNECTION_LOST:
							printf("Connection lost.\n");
							//RemoveNeighbour(packet->systemAddress.ToString());
							break;
							//recieving update from bootstrpa request 
						case RECIEVE_BOOT_STRAP_REPLY:
						{
														 Packet currentPacket = *packet;
														 DealWithBootStrap(currentPacket);
														 if ((portNumber - LISTENING_PORT) == 4)
														 {
															 RunUpdateProcedure();
														 }
						}
							break;

							//recieving a handshake request
						case  BOOT_HAND_SHAKE_REQUEST:
						{
														 DealHandShake(packet->systemAddress, packet->guid);
						}
							break;

						case HAND_SHAKE_INIT_UPDATE:
						{
													   UpdateHandShakeInit(GetStringFromBitStream(*packet), packet->systemAddress);
						}
							break;

						case HAND_SHAKE_REPLY_PEER:
						{
													  //recieve peers parse them 
													  UpdatePeerReplyer(GetStringFromBitStream(*packet), packet->systemAddress);
						}
							break;

						case SHARE_LOCAL_UPDATES:
						{
													ProcessUpdatesRecieved(GetStringFromBitStream(*packet), packet->systemAddress);
						}
							break;

						case SHARE_LOCAL_UPDATES_REPLY:
						{
														  ReplyToUpdate(GetStringFromBitStream(*packet), packet->systemAddress);
						}
							break;

						case TEST_MESSAGE:
							printf("Incoming message");
							break;

						default:
							//printf("Message with identifier %i has arrived. %s \n", packet->data[0], packet->data);
							break;
						}
					}
				}
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets random neighbour. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <returns>	The random neighbour. </returns>
			///-------------------------------------------------------------------------------------------------

			Neighbour& GetRandomNeighbour()
			{
				int randomNumber = rand() % neighbors.size();
				return neighbors[randomNumber];
			}


			//need to check it out

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Removes the neighbour described by neighboursAddress. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="neighboursAddress">	The neighbours address. </param>
			///-------------------------------------------------------------------------------------------------

			void RemoveNeighbour(std::string neighboursAddress)
			{
				int peerIndex = 0;
				bool result = FindNeighbour(SystemAddress(neighboursAddress.c_str()), peerIndex);

				if (result)
				{
					RemoveNeighbour(peerIndex);
				}
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Request connection. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="ipAddress">	The IP address. </param>
			///-------------------------------------------------------------------------------------------------

			void RequestConnection(std::string ipAddress)
			{
				peerData->Connect(ipAddress.c_str(), LISTENING_PORT, 0, 0);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets a neighbour. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="index">	Zero-based index of the. </param>
			///
			/// <returns>	The neighbour. </returns>
			///-------------------------------------------------------------------------------------------------

			Neighbour& GetNeighbour(int index)
			{
				if (index < neighbors.size() || index > 0)
				{
					return neighbors[index];
				}

				return Neighbour();
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets a neighbour. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="address">	[in,out] The address. </param>
			///
			/// <returns>	The neighbour. </returns>
			///-------------------------------------------------------------------------------------------------

			Neighbour& GetNeighbour(SystemAddress& address)
			{
				vector<Neighbour>::iterator iter = std::find(neighbors.begin(), neighbors.end(), Neighbour(address, RakNetGUID()));
				return *iter;

			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Sends the update. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///-------------------------------------------------------------------------------------------------

			void SendUpdate()
			{
				while (running)
				{
					//updater.ProduceUpdate(GetCurrentLogicalTime());
					std::string localUpdates = ParseNeighbours();
					updatesExist = false;
					RakNet::ConnectionAttemptResult result;
					Neighbour details;
					do
					{
						details = GetRandomNeighbour();

						std::string addressArray[2];
						HelperClass::ParseStringSentence(details.address.ToString(), "|", addressArray);
						result = peerData->Connect(addressArray[0].c_str(), boost::lexical_cast<unsigned short>(addressArray[1]), 0, 0);

					} while (result != 0);

					SendData(details, localUpdates, HAND_SHAKE_INIT_UPDATE);
					peerData->CloseConnection(details.address, true, 0, HIGH_PRIORITY);
					std::this_thread::sleep_for(std::chrono::microseconds(HelperClass::SetAmounTimeSleep(FIXED_AMOUNT_PEERS) * 100));
				}
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Sends a data. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="ipAddress">	[in,out] The IP address. </param>
			/// <param name="bS">			[in,out] The b s. </param>
			///-------------------------------------------------------------------------------------------------

			void SendData(SystemAddress& ipAddress, BitStream& bS)
			{
				//temp 
				std::lock_guard<std::mutex> lk(myMutex);
				peerData->Send(&bS, HIGH_PRIORITY, RELIABLE_ORDERED, 0, ipAddress, false);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Sends a data. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="currentNeighbour">	[in,out] The current neighbour. </param>
			/// <param name="bS">			   	[in,out] The b s. </param>
			///-------------------------------------------------------------------------------------------------

			void SendData(Neighbour& currentNeighbour, BitStream& bS)
			{
				SendData(currentNeighbour.address, bS);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Sends a data. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="currentNeighbour">	[in,out] The current neighbour. </param>
			/// <param name="stringToUse">	   	[in,out] The string to use. </param>
			/// <param name="id">			   	The identifier. </param>
			///-------------------------------------------------------------------------------------------------

			void SendData(Neighbour& currentNeighbour, std::string& stringToUse, MessageID id)
			{
				BitStream bS;
				bS.Write(MessageID(id));
				bS.Write(stringToUse.c_str());
				SendData(currentNeighbour.address, bS);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Sends a data. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="address">	  	[in,out] The address. </param>
			/// <param name="stringToUse">	[in,out] The string to use. </param>
			/// <param name="id">		  	The identifier. </param>
			///-------------------------------------------------------------------------------------------------

			void Peer::SendData(SystemAddress& address, std::string& stringToUse, MessageID id)
			{
				BitStream bS;
				bS.Write(MessageID(id));
				bS.Write(stringToUse.c_str());
				SendData(address, bS);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Use update. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="update">				The update. </param>
			/// <param name="address">				[in,out] The address. </param>
			/// <param name="connectionDetails">	[in,out] The connection details. </param>
			///-------------------------------------------------------------------------------------------------

			void Peer::UseUpdate(std::string update, SystemAddress& address, int& connectionDetails)
			{
				//add update to local daasrycture
				//_____________________________________________
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets local updates. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <returns>	The local updates. </returns>
			///-------------------------------------------------------------------------------------------------

			std::string GetLocalUpdates()
			{
				return "";
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Deal with boot strap. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="packet">	[in,out] The packet. </param>
			///-------------------------------------------------------------------------------------------------

			void DealWithBootStrap(Packet& packet)
			{
				RakString rs;
				BitStream bsIn(packet.data, packet.length, false);
				bsIn.IgnoreBytes(sizeof(RakNet::MessageID));
				bsIn.Read(rs);
				const char * temp = rs.C_String();
				boost::char_separator<char> sep(";");
				std::string output = std::string(temp);
				tokenizer tokens(output, sep);

				for (tokenizer::iterator iter = tokens.begin(); iter != tokens.end(); iter++)
				{
					ParseCurrentNeighbor(*iter);
				}
				int size = neighbors.size();
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Sort cache. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///-------------------------------------------------------------------------------------------------

			void SortCache()
			{
				std::sort(neighbors.begin(), neighbors.end());
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Increment logical time. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///-------------------------------------------------------------------------------------------------

			void IncrementLogicalTime()
			{
				logicalTimer++;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets current logical time. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <returns>	The current logical time. </returns>
			///-------------------------------------------------------------------------------------------------

			int GetCurrentLogicalTime()
			{
				return logicalTimer;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Parse neighbours. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <returns>	A std::string. </returns>
			///-------------------------------------------------------------------------------------------------

			std::string ParseNeighbours()
			{
				std::string output = "";
				for (int index = 0; index < neighbors.size(); index++)
				{
					output += neighbors[index].ToString();
				}
				return output;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Adds a new neighbours. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="stringNeighbours">	[in,out] The string neighbours. </param>
			///-------------------------------------------------------------------------------------------------

			void AddNewNeighbours(std::string stringNeighbours)
			{
				boost::char_separator<char> sep(";");
				std::string output = std::string(stringNeighbours);
				tokenizer tokens(output, sep);

				for (tokenizer::iterator iter = tokens.begin(); iter != tokens.end(); iter++)
				{
					ParseCurrentNeighbor(*iter);
				}
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Updates the hand shake initialise. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="peersString">	[in,out] The peers string. </param>
			/// <param name="address">	  	[in,out] The address. </param>
			///-------------------------------------------------------------------------------------------------

			void UpdateHandShakeInit(std::string peersString, SystemAddress& address)
			{
				//update time with respective timer 

				IncrementLogicalTime();
				SetTimerNeighbour(GetCurrentLogicalTime(), address);
				AddNewNeighbours(peersString);
				std::string stringNeighbours = ParseNeighbours();
				SendData(address, stringNeighbours, HAND_SHAKE_REPLY_PEER);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Updates the peer replyer. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="recievedUpdate">	[in,out] The recieved update. </param>
			/// <param name="address">		 	[in,out] The address. </param>
			///-------------------------------------------------------------------------------------------------

			void UpdatePeerReplyer(std::string recievedUpdate, SystemAddress& address)
			{
				IncrementLogicalTime();
				SetTimerNeighbour(GetCurrentLogicalTime(), address);
				AddNewNeighbours(recievedUpdate);
				//updater.updater.UpdatePeerTimer(GetCurrentLogicalTime());
				//std::string toReturnUpdates = updater.ParseUpdates();
				//SendData(address, toReturnUpdates, SHARE_LOCAL_UPDATES);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Process the updates recieved. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="recievedUpdates">	[in,out] The recieved updates. </param>
			/// <param name="address">		  	[in,out] The address. </param>
			///-------------------------------------------------------------------------------------------------

			void ProcessUpdatesRecieved(std::string recievedUpdates, SystemAddress& address)
			{
				IncrementLogicalTime();
				/*updater.UseRecivedUpdates(recievedUpdates);
				updater.updater.UpdatePeerTimer(GetCurrentLogicalTime());
				std::string toReturnUpdates = updater.ParseUpdates();
				SendData(address, toReturnUpdates, SHARE_LOCAL_UPDATES);*/
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Reply to update. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="recievedUpdates">	[in,out] The recieved updates. </param>
			/// <param name="address">		  	[in,out] The address. </param>
			///-------------------------------------------------------------------------------------------------

			void ReplyToUpdate(std::string recievedUpdates, SystemAddress& address)
			{
				/*IncrementLogicalTime();
				updater.UseRecivedUpdates(recievedUpdates);*/
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Sets timer neighbour. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="timer">  	[in,out] The timer. </param>
			/// <param name="address">	[in,out] The address. </param>
			///-------------------------------------------------------------------------------------------------

			void SetTimerNeighbour(int timer, SystemAddress& address)
			{
				int loc = 0;
				if (FindNeighbour(address, loc))
				{
					Neighbour& currentNeighbour = GetNeighbour(address);
					currentNeighbour.lastConnection = timer;
				}
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Executes the update procedure operation. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///-------------------------------------------------------------------------------------------------

			void RunUpdateProcedure()
			{
				//updater.ProduceUpdate(GetCurrentLogicalTime());
				std::string localPeers = ParseNeighbours();
				updatesExist = false;
				RakNet::ConnectionAttemptResult result;
				Neighbour details;
				do
				{
					details = GetRandomNeighbour();

					std::string addressArray[2];
					HelperClass::ParseStringSentence(details.address.ToString(), "|", addressArray);
					result = peerData->Connect(addressArray[0].c_str(), boost::lexical_cast<unsigned short>(addressArray[1]), 0, 0);

				} while (result != 0);

				SendData(details, localPeers, HAND_SHAKE_INIT_UPDATE);
				peerData->CloseConnection(details.address, true, 0, HIGH_PRIORITY);
				//std::this_thread::sleep_for(std::chrono::microseconds(HelperClass::SetAmounTimeSleep(FIXED_AMOUNT_PEERS) * 100));
			}

			RakPeerInterface *peerData;
			/// <summary>	true to running. </summary>
			bool running;
			/// <summary>	The neighbors. </summary>
			vector<Neighbour> neighbors;

		private:
			/// <summary>	The port number. </summary>
			int portNumber;

			/// <summary>	The logical timer. </summary>
			int logicalTimer;
			/// <summary>	The boot strap address. </summary>
			std::string bootStrapAddress;
			/// <summary>	true to updates exist. </summary>
			bool updatesExist;
			/// <summary>	true to new to network. </summary>
			bool newToNetwork;
			/// <summary>	The update thread. </summary>
			std::thread updateThread;
			/// <summary>	Zero-based index of the peer. </summary>
			int peerIndex;
			/// <summary>	my mutex. </summary>
			std::mutex myMutex;

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Writes to peer. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="peerId">	  	Identifier for the peer. </param>
			/// <param name="toWrite">	  	to write. </param>
			/// <param name="peerMessage">	Message describing the peer. </param>
			///
			/// <returns>	true if it succeeds, false if it fails. </returns>

			bool WriteToPeer(std::string peerId, std::string toWrite, PeerMessages peerMessage)
			{
				try
				{
					BitStream bS;
					bS.Write((RakNet::MessageID)peerMessage);
					bS.Write(toWrite.c_str());
					SendData(SystemAddress(peerId.c_str()), bS);
				}
				catch (std::exception e)
				{
					return false;
				}
			}
			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets random neighbors. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <returns>	The random neighbors. </returns>
			///-------------------------------------------------------------------------------------------------
			std::string GetRandomNeighbors()
			{
				int amountOfNeighbours = MAX_AMOUNT_PEER_TO_SEND;
				std::string output = "";

				if (neighbors.size() < amountOfNeighbours)
				{
					for (int index = 0; index < neighbors.size(); ++index)
					{
						output = output + neighbors[index].ToString() + ";";
					}
				}
				else
				{
					vector<int> toSend;
					for (int index = 0; index < MAX_AMOUNT_PEER_TO_SEND; ++index)
					{
						bool notContained = false;
						do
						{
							notContained = false;
							int randomNumber = rand() % neighbors.size();


						} while (!notContained);
					}

					for (int index = 0; index < MAX_AMOUNT_PEER_TO_SEND; ++index)
					{
						output = output + neighbors[index].ToString() + ";";
					}
				}
				return output;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Initialises the pool. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///-------------------------------------------------------------------------------------------------
			void InitPool()
			{
				/*int amountOfThreads = std::thread::hardware_concurrency();
				amountOfThreads -= 2;*/
				//threadPool = pool(1);
				//threadPool.create_thread(boost::bind(&io_service::run, &ioService));

				/*
				* This will assign tasks to the thread pool.
				* More about boost::bind: "http://www.boost.org/doc/libs/1_54_0/libs/bind/bind.html#with_functions"
				*/

				//adding tasks to queue
				//ioService.post(boost::bind(myTask, "Hello World!"));

			}
			///-------------------------------------------------------------------------------------------------
			/// <summary>	Initialises the listener. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///-------------------------------------------------------------------------------------------------

			void InitListener()
			{
				//listenerThread = std::thread(&ListenOnNetwork);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Parse current neighbor. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="currentNeighbor">	The current neighbor. </param>
			///-------------------------------------------------------------------------------------------------

			void ParseCurrentNeighbor(std::string currentNeighbor)
			{
				std::string myArray[2];
				HelperClass::ParseStringSentence(currentNeighbor, "-", myArray);
				AddNeighbor(RakNetGUID(std::atoi(myArray[0].c_str())), SystemAddress(myArray[1].c_str()));
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Parse system address. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="address">	[in,out] The address. </param>
			/// <param name="array">  	The array. </param>
			///-------------------------------------------------------------------------------------------------
			void ParseSystemAddress(SystemAddress& address, std::string array[])
			{
				HelperClass::ParseStringSentence(address.ToString(), "|", array);
			}
			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets string from bit stream. </summary>
			///
			/// <remarks>	Adrian, 31/08/2015. </remarks>
			///
			/// <param name="packet">	[in,out] The packet. </param>
			///
			/// <returns>	The string from bit stream. </returns>
			///-------------------------------------------------------------------------------------------------

			std::string GetStringFromBitStream(Packet p_packet)
			{
				RakString rs;
				BitStream bsIn(p_packet.data, p_packet.length, false);
				bsIn.IgnoreBytes(sizeof(RakNet::MessageID));
				bsIn.Read(rs);
				const char * temp = rs.C_String();

				return std::string(temp);
			}
		};
	}
}